import datetime
import hashlib

from django.conf import settings
from django.shortcuts import redirect, render

from . import forms
from . import models


# Create your views here.


def hash_code(s, salt='mysite'):
    # 通过构造函数获得一个hash对象.使用了sha256算法，加了点盐
    h = hashlib.sha256()
    s += salt
    # 使用hash对象的update方法添加消息
    # 注意了，update()方法现在只接受bytes类型的数据，不接收str类型。所有把str转换为bytes
    h.update(s.encode())
    return h.hexdigest()


def make_confirm_string(user):
    # 获取当前日期时间并格式化
    now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    # hash 这个user.name + 时间 作为code
    code = hash_code(user.name, now)
    # 通过django的ORM写数据到这个表login_confirmstring
    models.ConfirmString.objects.create(code=code, user=user)
    return code

# 发送多媒体邮件
def send_email(email, code):

    from django.core.mail import EmailMultiAlternatives

    subject = '来自www.xxxx.com的注册确认邮件'

    text_content = '''感谢注册www.xxxx.com，这里是刘江的博客和教程站点，专注于Python、Django和机器学习技术的分享！如果你看到这条消息，说明你的邮箱服务器不提供HTML链接功能，请联系管理员！'''

    html_content = '''
                    <p>感谢注册<a href="http://{}/confirm/?code={}" target=blank>www.xxxx.com</a>，\
                    这里是刘江的博客和教程站点，专注于Python、Django和机器学习技术的分享！</p>
                    <p>请点击站点链接完成注册确认！</p>
                    <p>此链接有效期为{}天！</p>
                    '''.format('127.0.0.1:8000', code, settings.CONFIRM_DAYS)

    msg = EmailMultiAlternatives(subject, text_content, settings.EMAIL_HOST_USER, [email])
    msg.attach_alternative(html_content, "text/html")
    msg.send()
# 首先我们需要导入settings配置文件from django.conf import settings。
#
# 邮件内容中的所有字符串都可以根据你的实际情况进行修改。其中关键在于<a href=''>中链接地址的格式，我
# 这里使用了硬编码的'127.0.0.1:8000'，请酌情修改，url里的参数名为code，它保存了关键的注册确认码，最后的有效期天数为设置在settings中的CONFIRM_DAYS。所有的这些都是可以定制的！
#
# 关于邮件注册，还有很多内容可以探讨，比如定时删除未在有效期内进行邮件确认的用户，这个可以用Django的celery实现，或者使用Linux的cronb功能。
#
# 关于邮件注册的工作逻辑，项目里只是抛砖引玉，做个展示，并不够严谨，也需要你自己根据实际环境去设计。
#
# 最后，其实Django生态圈有一个现成的邮件注册模块django-registration，但是这个模块灵活度不高，并且绑定了Auth框架，有兴趣的可以去看看其英文文档，中文资料较少。


def index(request):
    if not request.session.get('is_login', None):
        return redirect('/login/')
    return render(request, 'login/index.html')


def login(request):
    if request.session.get('is_login', None):  # 不允许重复登录
        return redirect('/index/')
    # 判断是否是POST请求
    if request.method == 'POST':
        # 空的form,用来渲染页面的
        login_form = forms.UserForm()

        # 将POST数据传给form对象,用传进来的数据构造对象
        login_form = forms.UserForm(request.POST)

        # 查看是否绑定form表单
        print(login_form.is_bound)
        message = '请检查填写的内容!'

        print(login_form.is_valid())

        # 校验login_form
        if login_form.is_valid():
            # 取验证过后的数据.因为cleaned_data这个返回的一个字典，所有key用get方法取对应的key
            username = login_form.cleaned_data.get('username')
            password = login_form.cleaned_data.get('password')

            try:
                # 判断这个user是否存在
                user = models.User.objects.get(name=username)
            except :
                # 如果不存在就再次重定向到login.html页面
                message = '用户不存在!'
                return render(request, 'login/login.html', locals())

            # 判断这个用户是否验证了邮箱
            if not user.has_confirmed:      #关键是的部分：
                message = '该用户还未经过邮件确认！'
                return render(request, 'login/login.html', locals())
            # 判断密码是否正确
            # 哈希值和数据库内的值进行比对
            if user.password == hash_code(password):
                # 正确后设置用户的session，然后跳转至index页面
                request.session['is_login'] = True
                request.session['user_id'] = user.id
                request.session['user_name'] = user.name
                return redirect('/index/')
            else:
                message = '密码不正确！'
                return render(request, 'login/login.html', locals())
        else:
            return render(request, 'login/login.html', locals())
    # 对于非POST方法发送数据时，比如GET方法请求页面，返回空的表单，让用户可以填入数据
    login_form = forms.UserForm()
    # 如果用户通过表单提交数据，但是数据验证、不合法，则渲染的是一个带有错误信息的表单
    return render(request, 'login/login.html', locals())
# 说明：
#
# 对于非POST方法发送数据时，比如GET方法请求页面，返回空的表单，让用户可以填入数据
# 对于POST方法，接收表单数据，并验证
# 使用表单类自带的is_valid() 方法一步完成数据验证工作
# 验证成功后可以从表单对象的cleaned_data数据字典中获取表单的具体值
# 如果验证不通过，则返回一个包含先前数据的表单给前端页面，方便用户修改。也就是说，他会帮你保留先前填写的数据内容，而不是返回一个空表！


# https://zhuanlan.zhihu.com/p/113739422  //点击刷新验证码



def register(request):
    # 判断是否已经登录了
    if request.session.get('is_login', None):
        return redirect('/index/')

    if request.method == 'POST':
        register_form = forms.RegisterForm(request.POST)
        message = "请检查填写的内容！"
        if register_form.is_valid():
            username = register_form.cleaned_data.get('username')
            password1 = register_form.cleaned_data.get('password1')
            password2 = register_form.cleaned_data.get('password2')
            email = register_form.cleaned_data.get('email')
            sex = register_form.cleaned_data.get('sex')

            if password1 != password2:
                message = '两次输入的密码不同！'
                return render(request, 'login/register.html', locals())
            else:
                same_name_user = models.User.objects.filter(name=username)
                # 用户名唯一
                if same_name_user:
                    message = '用户名已经存在'
                    return render(request, 'login/register.html', locals())
                # 邮箱唯一
                same_email_user = models.User.objects.filter(email=email)
                if same_email_user:
                    message = '该邮箱已经被注册了！'
                    return render(request, 'login/register.html', locals())

                # 当一切都OK的情况下，创建了新用户
                # 建议这里用django的ORM进行添加数据

                new_user = models.User()
                new_user.name = username
                new_user.password = hash_code(password1)
                new_user.email = email
                new_user.sex = sex
                # 需要进行save操作
                new_user.save()

                code = make_confirm_string(new_user)
                send_email(email, code)

                message = '请前往邮箱进行确认！'
                return render(request, 'login/confirm.html', locals())
        else:
            return render(request, 'login/register.html', locals())
    register_form = forms.RegisterForm()
    return render(request, 'login/register.html', locals())


def logout(request):
    if not request.session.get('is_login', None):
        # 如果没登录，也就没有登出一说
        return redirect('/login/')

    request.session.flush()
    # 或者使用下面的方法
    # del request.session['is_login']
    # del request.session['user_id']
    # del request.session['user_name']
    return redirect("/login/")


def user_confirm(request):
    code = request.GET.get('code', None)
    message = ''

    try:
        confirm = models.ConfirmString.objects.get(code=code)
    except:
        message = '无效的确认请求！'
        return render(request, 'login/confirm.html', locals())

    c_time = confirm.c_time
    now = datetime.datetime.now()
    if now > c_time + datetime.timedelta(settings.CONFIRM_DAYS):
        confirm.user.delete()
        message = '您的邮件已经过期！请重新注册！'
        return render(request, 'login/confirm.html', locals())
    else:
        confirm.user.has_confirmed = True
        confirm.user.save()
        confirm.delete()
        message = '感谢确认，请使用账户登录！'
        return render(request, 'login/confirm.html', locals())
#
# 说明：
#
# 通过request.GET.get('code', None)从请求的url地址中获取确认码;
# 先去数据库内查询是否有对应的确认码;
# 如果没有，返回confirm.html页面，并提示;
# 如果有，获取注册的时间c_time，加上设置的过期天数，这里是7天，然后与现在时间点进行对比；
# 如果时间已经超期，删除注册的用户，同时注册码也会一并删除，然后返回confirm.html页面，并提示;
# 如果未超期，修改用户的has_confirmed字段为True，并保存，表示通过确认了。然后删除注册码，但不删除用户本身。最后返回confirm.html页面，并提示


